package com.lagou.rpc.consumer.proxy;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.lagou.rpc.common.RpcRequest;
import com.lagou.rpc.common.RpcResponse;
import com.lagou.rpc.common.ZooNodeData;
import com.lagou.rpc.consumer.client.RpcClient;
import com.lagou.rpc.consumer.config.StaticVariable;
import org.I0Itec.zkclient.ZkClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 客户端代理类-创建代理对象
 * 1.封装request请求对象
 * 2.创建RpcClient对象
 * 3.发送消息
 * 4.返回结果
 */
@Component
public class RpcClientProxy {

    public static void main(String[] args) {
        String s = JSON.parseObject("\"127.0.0.1:8080\"", String.class);
        System.out.println(s);

        ZooNodeData zooNodeData = ZooNodeData
                .builder()
                .responseDelay(0)
                .lastRequestTime(System.currentTimeMillis())
                .build();
        System.out.println(JSON.toJSONString(zooNodeData));
    }

    private static int count = 0;

    @Autowired
    private ZkClient zkClient;

    @Value("${zookeeper.grand-path}")
    private String grandPath;

    public Object createProxy(Class serviceClass) {

        return Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{serviceClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        RpcClient rpcClient;
                        RpcRequest rpcRequest = new RpcRequest();
                        long startTime = System.currentTimeMillis();
                        long endTime = 0;
                        //todo 首先从zookeeper获取所有节点的延迟信息
                        List<ZooNodeData> allZooNodeData = getAllZooNodeData();
                        System.out.println(allZooNodeData);
                        //todo 决定选择哪一台服务器
                        ZooNodeData decidedServer = decideServer(allZooNodeData);
                        //首先判断是否为关闭服务器请求
                        if ("shutDownServer".equals(method.getName())) {

//                            RpcRequest rpcRequest = new RpcRequest();
                            //设置rpcRequest的status是500
                            rpcRequest.setStatus(500);
                            rpcRequest.setRequestId(UUID.randomUUID().toString());
                            rpcRequest.setClassName(method.getDeclaringClass().getName());
                            rpcRequest.setMethodName(method.getName());
                            rpcRequest.setParameterTypes(method.getParameterTypes());
                            rpcRequest.setParameters(args);

                            //根据参数寻找需要关闭的服务器
                            rpcClient = new RpcClient((String) args[0], (Integer) args[1]);
                        }
                        //如果是请求用户的请求就继续
                        else {


                            //1.封装request请求对象
//                            RpcRequest rpcRequest = new RpcRequest();
                            //设置rpcRequest的status是200
                            rpcRequest.setStatus(200);
                            rpcRequest.setRequestId(UUID.randomUUID().toString());
                            rpcRequest.setClassName(method.getDeclaringClass().getName());
                            System.out.println(method.getName());
                            rpcRequest.setMethodName(method.getName());
                            rpcRequest.setParameterTypes(method.getParameterTypes());
                            rpcRequest.setParameters(args);
                            //2.创建RpcClient对象

                            //todo 需要根据 providerList获取对应的负载均衡策略
                            if (StaticVariable.providerInfoList.size() == 0) {
                                throw new RuntimeException("所有服务都下线了");
                            }

                            rpcClient = new RpcClient(decidedServer.getIp(), decidedServer.getPort());

                        }


                        try {
                            //3.发送消息
                            Object responseMsg = rpcClient.send(JSON.toJSONString(rpcRequest));
                            RpcResponse rpcResponse = JSON.parseObject(responseMsg.toString(), RpcResponse.class);
                            if (rpcResponse.getError() != null) {
                                throw new RuntimeException(rpcResponse.getError());
                            }
                            //4.返回结果
                            Object result = rpcResponse.getResult();

                            //todo 每次请求更新时延和最后请求时间
                            endTime = System.currentTimeMillis();
                            if (!"shutDownServer".equals(method.getName())) {
                                long delay = endTime - startTime;
                                ZooNodeData zooNodeData = zkClient.readData(grandPath + "/provider_" + decidedServer.getIp() + ":" + decidedServer.getPort());
                                zooNodeData.setLastRequestTime(endTime);
                                zooNodeData.setResponseDelay(delay);
                                zkClient.writeData(grandPath + "/provider_" + decidedServer.getIp() + ":" + decidedServer.getPort(), zooNodeData);

                            }


                            return JSON.parseObject(result.toString(), method.getReturnType());
                        } catch (Exception e) {
                            throw e;
                        } finally {
                            rpcClient.close();
                        }

                    }
                });
    }

    private List<ZooNodeData> getAllZooNodeData() {
        List<String> allNodes = zkClient.getChildren(grandPath);
        List<ZooNodeData> nodeDataList = new ArrayList<>();
        for (String oneNode : allNodes) {
            ZooNodeData oneNodeData = zkClient.readData(grandPath + "/" + oneNode);
            nodeDataList.add(oneNodeData);
        }
        return nodeDataList;
    }

    private ZooNodeData decideServer(List<ZooNodeData> nodeDataList) {
        ZooNodeData candidateNode = nodeDataList.get(0);
        for (ZooNodeData zooNode : nodeDataList) {
            if (zooNode.getResponseDelay() < candidateNode.getResponseDelay()) {
                candidateNode = zooNode;
            }
        }
        return candidateNode;
    }
}
